package reports

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"

	"github.com/tealeg/xlsx"
)

//Xlsx excel导出类
type Xlsx struct {
	sheet   *xlsx.Sheet
	file    *xlsx.File
	style   *xlsx.Style
	columns []Column
	leafs   []Column
}

//NewXlsx 新建excel导出类
func NewXlsx() (*Xlsx, error) {
	file := xlsx.NewFile()
	sheet, err := file.AddSheet("Sheet1")
	if err != nil {
		return nil, err
	}
	style := xlsx.Style{
		Border: *xlsx.NewBorder("thin", "thin", "thin", "thin"),
		Font:   *xlsx.NewFont(12, "宋体"),
	}
	style.Alignment.Horizontal = "center"
	style.Alignment.Vertical = "center"
	style.Font.Bold = false
	return &Xlsx{
		file:  file,
		sheet: sheet,
		style: &style,
	}, nil
}

//Export 导出Xlsx数据
func (x *Xlsx) Export() *bytes.Buffer {
	buff := new(bytes.Buffer)
	x.file.Write(buff)
	return buff
}

//AddColumns 添加列头
func (x *Xlsx) AddColumns(columns []Column) {
	maxDeep := 0
	for _, col := range columns {
		deep := x.getColumnDeep(&col)
		if deep > maxDeep {
			maxDeep = deep
		}
	}
	for i := 0; i < maxDeep; i++ {
		x.sheet.AddRow()
	}
	for _, col := range columns {
		x.exportColumn(&col, 1, maxDeep)
	}
	x.columns = columns
	leafs := make([]Column, 0, 20)
	for _, col := range x.columns {
		lfs := x.getColumnleafs(&col)
		leafs = append(leafs, lfs...)
	}
	x.leafs = leafs
}

//AddRow 导出行
func (x *Xlsx) AddRow(rowData map[string]string) error {
	row := x.sheet.AddRow()
	for _, col := range x.leafs {
		cell := row.AddCell()
		style := xlsx.Style{
			Border: *xlsx.NewBorder("thin", "thin", "thin", "thin"),
			Font:   *xlsx.NewFont(12, "宋体"),
		}
		if col.DataType == "float" || col.DataType == "int" || col.DataType == "number" {
			style.Alignment.Horizontal = "right"
			numVal := rowData[col.DataIndex]
			if numVal != "" {
				if col.DataType == "float" || col.DataType == "number" {
					fVal, err := strconv.ParseFloat(numVal, 64)
					if err != nil {
						return fmt.Errorf("报表列%s类型为number，值%s无效！", col.Name, numVal)
					}
					cell.SetFloat(fVal)
					format := col.Format
					if format == "" {
						format = ".2f"
					}
					isP := strings.HasSuffix(format, "f%")
					index := len(format) - 1
					if isP {
						index--
					}
					zNumStr := string(([]byte(format))[1:index])
					zNum, err := strconv.Atoi(zNumStr)
					if err != nil {
						return fmt.Errorf("报表列%s的format定义的%s是无效！", col.Name, format)
					}
					zNumArr := make([]string, zNum, zNum)
					for i := 0; i < zNum; i++ {
						zNumArr[i] = "0"
					}
					zeroExp := strings.Join(zNumArr, "")
					if isP {
						cell.NumFmt = fmt.Sprintf("0.%s", zeroExp) + "%"
					} else {
						cell.NumFmt = fmt.Sprintf("#,##0.%s;[red]-#,##0.%s", zeroExp, zeroExp)
					}
				} else {
					dVal, err := strconv.Atoi(numVal)
					if err != nil {
						return fmt.Errorf("报表列%s类型为int，值%s无效！", col.Name, numVal)
					}
					cell.SetInt(dVal)
					cell.NumFmt = "#,##0 ;[red]-#,##0"
				}
			}
		} else {
			style.Alignment.Horizontal = "left"
			cell.SetValue(rowData[col.DataIndex])
		}
		cell.SetStyle(&style)
	}
	return nil
}
func (x *Xlsx) exportColumn(column *Column, level, maxDeep int) {
	row := x.sheet.Rows[level-1]
	cell := row.AddCell()
	rowSpan := x.getColumnCount(column) - 1
	colSpan := 1
	cols := column.Children
	hasChildren := true
	if cols == nil || len(cols) == 0 {
		colSpan = maxDeep - level
		hasChildren = false
	}
	cell.Merge(rowSpan, colSpan)
	cell.SetValue(column.Title)
	cell.SetStyle(x.style)
	if hasChildren {
		level++
		for _, ccol := range cols {
			x.exportColumn(&ccol, level, maxDeep)
		}
	}
}

func (x *Xlsx) getColumnleafs(column *Column) []Column {
	leafs := make([]Column, 0, 5)
	cols := column.Children
	if cols == nil || len(cols) == 0 {
		leafs = append(leafs, *column)
	}
	for _, ccol := range cols {
		l2 := x.getColumnleafs(&ccol)
		leafs = append(leafs, l2...)
	}
	return leafs
}

func (x *Xlsx) getColumnCount(column *Column) int {
	count := 0
	cols := column.Children
	if cols == nil || len(cols) == 0 {
		return 1
	}
	for _, ccol := range cols {
		c2 := x.getColumnCount(&ccol)
		count += c2
	}
	return count
}
func (x *Xlsx) getColumnDeep(column *Column) int {
	cols := column.Children
	if cols == nil || len(cols) == 0 {
		return 1
	}
	deep := 0
	for _, col := range cols {
		d2 := x.getColumnDeep(&col)
		if d2 > deep {
			deep = d2
		}
	}
	return deep
}
